/*
 * Copyright 2010 Guillaume Amringer
 * 
 * This file is part of WLoader.
 * 
 * WLoader is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * WLoader is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with WLoader.  If not, see <http://www.gnu.org/licenses/>.
 * */
package ga.wloader{
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import ga.wloader.events.WNetEvent;
	import ga.wloader.net.WURLRequest;
	
	public class WLoader extends EventDispatcher {

		private static var _instances:Object = { };
		private var _urls:Object = { };
		private var _loadedItems:Array = [];
		private var _loadedCount:uint = 0;
		private var _readyCount:uint = 0;
		private var _idIncrementor:uint = 0;
		private var _requestCount:uint = 0;
		private var _requestErrorCount:uint = 0;
		
		public var bytesTotal:uint = 0;
		public var bytesLoaded:uint = 0;
		
		public var instanceName:String;
		
		private var _perRequestBytesLoaded:Object = { };
		
		public function WLoader(pInstanceName:String = "default") {
			
			instanceName = pInstanceName;
			
			if (WLoader._instances[instanceName] != undefined) {
				trace("Loader instance "+ instanceName +" already exists");
				
				return;
			}else {
				WLoader._instances[instanceName] = this;
			}
		}
		
		public override function toString():String {
			return 'WLoader(' + instanceName + '): BytesLoaded = ' + bytesLoaded +
			' | BytesTotal = ' + bytesTotal +
			' | Requests = ' + _requestCount +
			' | Ready = ' + _readyCount +
			' | Loaded = ' + _loadedCount +
			' | Error = ' + _requestErrorCount;
		}
		
		public function getRequest(id:String):WURLRequest {
			return _urls[id];
		}
		
		public function hasLoaded(id:String):Boolean {
			return _loadedItems[id] != undefined;
		}
		
		public static function getLoader(pInstanceName:String):WLoader {
			if (WLoader._instances[pInstanceName] != undefined) {
				return WLoader._instances[pInstanceName];
			}else {
				return new WLoader(pInstanceName);
			}
		}

		public function addURL(pUrl:String, options:Object = null):void {
			
			if (options == null) {
				options = { };
			}

			options.url = pUrl;
			
			if (options.id == null) {
				options.id = "item" + (++ _idIncrementor);
			}
			
			if (_urls[options.id] != null) {
				trace("Item with that ID already exists");
			}
			
			_urls[options.id] = new WURLRequest(options);
			
			_requestCount++;
		}
		
		public function start():void {
			for each(var request:WURLRequest in _urls) {
				request.addEventListener(ProgressEvent.PROGRESS, _urlProgressHandler);
				request.addEventListener(WNetEvent.META_DATA, _urlMetaDataHandler);
				request.addEventListener(Event.COMPLETE, _urlCompleteHandler);
				request.addEventListener(WNetEvent.READY, _urlReadyHandler);
				request.addEventListener(IOErrorEvent.IO_ERROR, _ioErrorHandler);
				request.addEventListener(SecurityErrorEvent.SECURITY_ERROR, _securityErrorHandler);
				request.start();
			}
		}
		
		private function _urlProgressHandler(e:ProgressEvent):void {
			bytesLoaded += e.target.bytesLoaded - _perRequestBytesLoaded[e.target.id];
			_perRequestBytesLoaded[e.target.id] = e.target.bytesLoaded;
			
			this.dispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS));
		}
		
		private function _urlMetaDataHandler(e:WNetEvent):void {
			bytesTotal += e.target.bytesTotal;
			_perRequestBytesLoaded[e.target.id] = 0;
		}
		
		private function _urlCompleteHandler(e:Event):void {
			e.target.close();
			e.target.removeEventListener(Event.COMPLETE, _urlCompleteHandler);
			_loadedItems.push(e.target.id);
			_loadedCount++;
			
			this.dispatchEvent(new WNetEvent(WNetEvent.ITEM_LOADED, e.target));
			
			if (_requestCount - _requestErrorCount == _loadedCount) {
				this.dispatchEvent(new WNetEvent(WNetEvent.LOADED));
			}
		}
		
		private function _urlReadyHandler(e:Event):void {
			e.target.removeEventListener(WNetEvent.READY, _urlReadyHandler);
			_readyCount++;
			
			this.dispatchEvent(new WNetEvent(WNetEvent.ITEM_READY, e.target));
			
			if (_requestCount - _requestErrorCount == _readyCount) {
				this.dispatchEvent(new WNetEvent(WNetEvent.READY));
			}
		}
		
		private function _ioErrorHandler(e:IOErrorEvent):void {
			_requestErrorCount++;
			e.target.removeEventListener(IOErrorEvent.IO_ERROR, _ioErrorHandler);
			trace(e.target.id +" could not download: Fatal Error. Does the file exist ?", e);
		}
		
		private function _securityErrorHandler(e:SecurityErrorEvent):void {
			_requestErrorCount++;
			e.target.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, _securityErrorHandler);
			trace(e.target.id +" could not download: Security Problem. Check Domains.", e);
		}
	}
}
